تعرف على المفاهيم الأساسية وتقنيات العرض المتقدمة للظلال في الوقت الفعلي في WebGL. يغطي هذا الدليل تظليل الخرائط، PCF، CSM، والحلول للعيوب الشائعة.
تظليل الويب جي إل: دليل شامل للعرض في الوقت الفعلي
في عالم رسومات الكمبيوتر ثلاثية الأبعاد، نادرًا ما تساهم عناصر أخرى في الواقعية والانغماس مثل الظلال. إنها توفر إشارات بصرية حاسمة حول العلاقات المكانية بين الكائنات، وموقع مصادر الضوء، والهندسة العامة للمشهد. بدون الظلال، يمكن أن تبدو العوالم ثلاثية الأبعاد مسطحة، ومنفصلة، ومصطنعة. بالنسبة للتطبيقات ثلاثية الأبعاد المستندة إلى الويب والتي تعمل بـ WebGL، فإن تطبيق ظلال عالية الجودة في الوقت الفعلي هو علامة مميزة للتجارب الاحترافية. يقدم هذا الدليل نظرة متعمقة على التقنية الأكثر أساسية والأكثر استخدامًا لتحقيق ذلك: تظليل الخرائط.
سواء كنت مبرمج رسومات متمرسًا أو مطور ويب يتجه إلى البعد الثالث، فإن هذه المقالة ستزودك بالمعرفة لفهم وتطبيق وتصحيح أخطاء الظلال في الوقت الفعلي في مشاريع WebGL الخاصة بك. سننتقل من النظرية الأساسية إلى تفاصيل التنفيذ العملي، مستكشفين المشاكل الشائعة والتقنيات المتقدمة المستخدمة في محركات الرسومات الحديثة.
الفصل الأول: أساسيات تظليل الخرائط
في جوهرها، تظليل الخرائط هي تقنية ذكية وأنيقة تحدد ما إذا كانت نقطة ما في المشهد في الظل من خلال طرح سؤال بسيط: "هل يمكن رؤية هذه النقطة بواسطة مصدر الضوء؟" إذا كانت الإجابة لا، فهذا يعني أن شيئًا ما يعيق الضوء، ويجب أن تكون النقطة في الظل. للإجابة على هذا السؤال برمجيًا، نستخدم نهج عرض من تمريرتين.
ما هو تظليل الخرائط؟ المفهوم الأساسي
تعتمد التقنية بأكملها على عرض المشهد مرتين، كل مرة من منظور مختلف:
- التمريرة الأولى: تمريرة العمق (من منظور الضوء). أولاً، نعرض المشهد بأكمله من الموضع والاتجاه الدقيقين لمصدر الضوء. ومع ذلك، لا نهتم بالألوان أو القوام في هذه التمريرة. المعلومات الوحيدة التي نحتاجها هي العمق. لكل كائن يتم عرضه، نسجل المسافة منه إلى مصدر الضوء. يتم تخزين هذه المجموعة من قيم العمق في نسيج خاص يسمى خريطة الظل أو خريطة العمق. يمثل كل بكسل في هذه الخريطة المسافة إلى أقرب كائن من وجهة نظر الضوء في اتجاه معين.
- التمريرة الثانية: تمريرة المشهد (من منظور الكاميرا). بعد ذلك، نعرض المشهد كما نفعل عادةً، من منظور الكاميرا الرئيسية. ولكن لكل بكسل يتم رسمه، نجري حسابًا إضافيًا. نحدد موضع هذا البكسل في الفضاء ثلاثي الأبعاد ثم نسأل: "ما مدى بعد هذه النقطة عن مصدر الضوء؟" ثم نقارن هذه المسافة بالقيمة المخزنة في خريطة الظل الخاصة بنا (من التمريرة الأولى) في الموقع المقابل.
المنطق بسيط:
- إذا كانت المسافة الحالية للبكسل عن الضوء أكبر من المسافة المخزنة في خريطة الظل، فهذا يعني أن هناك كائنًا آخر أقرب إلى الضوء على طول خط الرؤية نفسه. لذلك، فإن البكسل الحالي في الظل.
- إذا كانت مسافة البكسل أقل من أو تساوي المسافة في خريطة الظل، فهذا يعني أن شيئًا لا يعيقها، والبكسل مضاء بالكامل.
إعداد المشهد
لتنفيذ تظليل الخرائط في WebGL، تحتاج إلى عدة مكونات رئيسية:
- مصدر ضوء: يمكن أن يكون ضوءًا اتجاهيًا (مثل الشمس)، أو ضوء نقطة (مثل مصباح)، أو ضوء كشاف. سيحدد نوع الضوء نوع مصفوفة الإسقاط المستخدمة أثناء تمريرة العمق.
- كائن إطار (FBO): يقوم WebGL عادةً بالعرض إلى إطار الشاشة الافتراضي. لإنشاء خريطة الظل الخاصة بنا، نحتاج إلى هدف عرض خارج الشاشة. يسمح لنا FBO بالعرض في نسيج بدلاً من الشاشة. سيتم تكوين FBO الخاص بنا مع مرفق نسيج عمق.
- مجموعتين من الظلال: ستحتاج إلى برنامج ظلال واحد لتمريرة العمق (بسيط جدًا) وبرنامج آخر لتمريرة المشهد النهائية (التي ستحتوي على منطق حساب الظل).
- المصفوفات: ستحتاج إلى مصفوفات النموذج والعرض والإسقاط القياسية للكاميرا. والأهم من ذلك، ستحتاج أيضًا إلى مصفوفة عرض وإسقاط لمصدر الضوء، وغالبًا ما يتم دمجها في "مصفوفة مساحة الضوء" واحدة.
الفصل الثاني: خط أنابيب العرض ذي المرورات المزدوجة بالتفصيل
دعنا نقسم تمريرات العرض المزدوجة خطوة بخطوة، مع التركيز على أدوار المصفوفات والظلال.
التمريرة الأولى: تمريرة العمق (من منظور الضوء)
هدف هذه التمريرة هو ملء نسيج العمق الخاص بنا. إليك كيف تعمل:
- ربط FBO: قبل الرسم، تقوم بتعليم WebGL بالعرض إلى FBO المخصص الخاص بك بدلاً من اللوحة القماشية.
- تكوين منفذ العرض: قم بتعيين أبعاد منفذ العرض لتتناسب مع حجم نسيج خريطة الظل الخاصة بك (على سبيل المثال، 1024 × 1024 بكسل).
- مسح مخزن العمق: تأكد من مسح مخزن العمق الخاص بـ FBO قبل العرض.
- إنشاء مصفوفات الضوء:
- مصفوفة عرض الضوء: هذه المصفوفة تحول العالم إلى منظور الضوء. بالنسبة للضوء الاتجاهي، يتم إنشاؤه عادةً باستخدام وظيفة `lookAt`، حيث يكون "العين" هو موضع الضوء و"الهدف" هو الاتجاه الذي يشير إليه.
- مصفوفة إسقاط الضوء: بالنسبة للضوء الاتجاهي، الذي يحتوي على أشعة متوازية، يتم استخدام إسقاط متعامد. بالنسبة لأضواء النقطة أو أضواء الكشاف، يتم استخدام إسقاط منظور. تحدد هذه المصفوفة الحجم في الفضاء (صندوق أو مخروط) الذي سيُلقي الظلال.
- استخدام برنامج ظلال العمق: هذا هو الحد الأدنى من الظلال. وظيفة shader الرأسية الوحيدة هي ضرب موضع الرأس بالمصفوفات عرض وإسقاط الضوء. shader الأجزاء أبسط: كل ما يفعله هو كتابة قيمة عمق الجزء (إحداثي z الخاص به) في نسيج العمق. في WebGL الحديث، غالبًا ما لا تحتاج حتى إلى shader أجزاء مخصص، حيث يمكن تكوين FBO لالتقاط مخزن العمق تلقائيًا.
- عرض المشهد: ارسم جميع الكائنات التي تلقي الظلال في مشهدك. يحتوي FBO الآن على خريطة الظل المكتملة لدينا.
التمريرة الثانية: تمريرة المشهد (من منظور الكاميرا)
الآن نعرض الصورة النهائية، باستخدام خريطة الظل التي أنشأناها لتحديد الظلال.
- فك ربط FBO: قم بالتبديل مرة أخرى للعرض إلى إطار اللوحة القماشية الافتراضي.
- تكوين منفذ العرض: قم بتعيين منفذ العرض مرة أخرى إلى أبعاد اللوحة القماشية.
- مسح الشاشة: امسح مخازن الألوان والعمق للوحة القماشية.
- استخدام برنامج ظلال المشهد: هذا هو المكان الذي يحدث فيه السحر. هذا الظل أكثر تعقيدًا.
- shader الرأسية: يجب أن يقوم هذا الظل بأمرين. أولاً، يحسب موضع الرأس النهائي باستخدام مصفوفات النموذج والعرض والإسقاط للكاميرا كالمعتاد. ثانيًا، يجب عليه أيضًا حساب موضع الرأس من منظور الضوء باستخدام مصفوفة مساحة الضوء من التمريرة الأولى. يتم تمرير هذا الإحداثي الثاني إلى shader الأجزاء كمتغير.
- shader الأجزاء: هذا هو جوهر منطق الظل. لكل جزء:
- استلم الموضع المستقطب في مساحة الضوء من shader الرأسية.
- قم بإجراء قسمة منظور على هذا الإحداثي (اقسم x و y و z على w). هذا يحوله إلى إحداثيات الجهاز الموحدة (NDC)، تتراوح من -1 إلى 1.
- حول NDC إلى إحداثيات نسيج (التي تتراوح من 0 إلى 1) حتى نتمكن من أخذ عينات من خريطة الظل الخاصة بنا. هذه عملية قياس وتحيز بسيطة: `texCoord = ndc * 0.5 + 0.5;`.
- استخدم إحداثيات النسيج هذه لأخذ عينات من نسيج خريطة الظل الذي تم إنشاؤه في التمريرة الأولى. هذا يعطينا `depthFromShadowMap`.
- عمق الجزء الحالي من منظور الضوء هو مكون z لإحداثي مساحة الضوء المحول. دعنا نسميه `currentDepth`.
- قارن الأعماق: إذا كان `currentDepth > depthFromShadowMap`، فإن الجزء في الظل. سنحتاج إلى إضافة تحيز صغير لهذا الفحص لتجنب مشكلة تسمى "حب الشباب الظل"، والتي سنتحدث عنها لاحقًا.
- بناءً على المقارنة، حدد عامل ظل (على سبيل المثال، 1.0 للإضاءة، 0.3 للظل).
- طبق عامل الظل هذا على حساب اللون النهائي (على سبيل المثال، اضرب مكونات الإضاءة المحيطة والمنتشرة في عامل الظل).
- عرض المشهد: ارسم جميع الكائنات في المشهد.
الفصل الثالث: مشاكل وحلول شائعة
سيؤدي تطبيق تظليل الخرائط الأساسي بسرعة إلى الكشف عن العديد من العيوب البصرية الشائعة. يعد فهمها وإصلاحها أمرًا بالغ الأهمية لتحقيق نتائج عالية الجودة.
حب الشباب الظل (عيوب الظل الذاتي)
المشكلة: قد ترى أنماطًا غريبة وغير صحيحة من الخطوط الداكنة أو الأنماط الشبيهة بـ Moiré على الأسطح التي يجب أن تكون مضاءة بالكامل. هذا ما يسمى "حب الشباب الظل". يحدث ذلك لأن قيمة العمق المخزنة في خريطة الظل وقيمة العمق المحسوبة أثناء تمريرة المشهد هي لنفس السطح . نظرًا لعدم دقة الفاصلة العائمة والدقة المحدودة لخريطة الظل، يمكن للأخطاء الصغيرة أن تسبب تحديدًا غير صحيح للجزء الذي يقع خلف نفسه، مما يؤدي إلى الظل الذاتي.
الحل: تحيز العمق. الحل الأبسط هو إدخال تحيز صغير إلى `currentDepth` قبل المقارنة. بجعل الجزء يبدو أقرب قليلاً إلى الضوء مما هو عليه في الواقع، فإننا ندفعه "خارج" ظله.
float shadow = currentDepth > depthFromShadowMap + bias ? 0.3 : 1.0;
يعد العثور على قيمة التحيز الصحيحة توازنًا دقيقًا. صغير جدًا، ويبقى حب الشباب. كبير جدًا، وستحصل على المشكلة التالية.
بطل بيتر (Peter Panning)
المشكلة: يتجلى هذا الخلل، الذي سمي على اسم الشخصية التي يمكنها الطيران وفقدت ظلها، على شكل فجوة مرئية بين كائن وظله. يجعل الكائنات تبدو وكأنها تطفو أو منفصلة عن الأسطح التي يجب أن تستقر عليها. إنها نتيجة مباشرة لاستخدام تحيز عمق كبير جدًا.
الحل: تحيز عمق التدرج. حل أكثر قوة من تحيز ثابت هو جعل التحيز يعتمد على مدى انحدار السطح بالنسبة للضوء. المضلعات الأكثر انحدارًا أكثر عرضة لحب الشباب وتتطلب تحيزًا أكبر. المضلعات الأكثر تسطحًا تحتاج إلى تحيز أصغر. توفر معظم واجهات برمجة تطبيقات الرسومات، بما في ذلك WebGL، وظائف لتطبيق هذا النوع من التحيز تلقائيًا أثناء تمريرة العمق، وهو ما يفضل بشكل عام على التحيز اليدوي في shader الأجزاء.
تشويش المنظور (حواف متعرجة)
المشكلة: تبدو حواف الظلال الخاصة بك مربعة، متعرجة، ومنقطة. هذا شكل من أشكال التشويش. يحدث لأن دقة خريطة الظل محدودة. قد يغطي البكسل الواحد (أو texel) في خريطة الظل مساحة كبيرة على سطح في المشهد النهائي، خاصة بالنسبة للأسطح القريبة من الكاميرا أو تلك التي يتم عرضها بزاوية مائلة. يسبب عدم تطابق الدقة هذا المظهر المربع المميز.
الحل: زيادة دقة خريطة الظل (على سبيل المثال، من 1024 × 1024 إلى 4096 × 4096) يمكن أن يساعد، ولكنه يأتي بتكلفة كبيرة في الذاكرة والأداء ولا يحل المشكلة الأساسية بالكامل. تكمن الحلول الحقيقية في التقنيات الأكثر تقدمًا.
الفصل الرابع: تقنيات تظليل الخرائط المتقدمة
يوفر تظليل الخرائط الأساسي أساسًا، ولكن التطبيقات الاحترافية تستخدم خوارزميات أكثر تطورًا للتغلب على قيودها، وخاصة التشويش.
تصفية النسبة المئوية الأقرب (PCF)
PCF هي التقنية الأكثر شيوعًا لتنعيم حواف الظل وتقليل التشويش. بدلاً من أخذ عينة واحدة من خريطة الظل واتخاذ قرار ثنائي (في الظل أو غير في الظل)، يأخذ PCF عينات متعددة من المنطقة المحيطة بالإحداثي المستهدف.
المفهوم: لكل جزء، نأخذ عينات من خريطة الظل ليس مرة واحدة فقط، ولكن في نمط شبكي (على سبيل المثال، 3 × 3 أو 5 × 5) حول إحداثي النسيج المسقط للجزء. لكل عينة من هذه العينات، نجري مقارنة العمق. قيمة الظل النهائية هي متوسط جميع هذه المقارنات. على سبيل المثال، إذا كانت 4 من أصل 9 عينات في الظل، فسيتم تظليل الجزء بنسبة 4/9، مما ينتج عنه منطقة شبحية ناعمة (الحافة الناعمة للظل).
التنفيذ: يتم كل هذا داخل shader الأجزاء. يتضمن حلقة تكرر عبر نواة صغيرة، وتأخذ عينات من خريطة الظل عند كل إزاحة وتجمع النتائج. يوفر WebGL 2 دعمًا للأجهزة (`texture` مع `sampler2DShadow`) الذي يمكنه إجراء المقارنة والتصفية بكفاءة أكبر.
الفائدة: يحسن بشكل كبير جودة الظل عن طريق استبدال الحواف الصلبة والمشوشة بحواف ناعمة.
التكلفة: ينخفض الأداء مع زيادة عدد العينات المأخوذة لكل جزء.
خرائط الظل المتتالية (CSM)
CSM هو الحل القياسي في الصناعة لعرض الظلال من مصدر ضوء اتجاهي واحد (مثل الشمس) عبر مشهد كبير جدًا. إنه يعالج مباشرة مشكلة تشويش المنظور.
المفهوم: الفكرة الأساسية هي أن الكائنات القريبة من الكاميرا تحتاج إلى دقة ظل أعلى بكثير من الكائنات البعيدة. يقسم CSM مخروط رؤية الكاميرا إلى عدة أقسام، أو "تتاليات"، على طول عمقه. ثم يتم عرض خريطة ظل منفصلة وعالية الجودة لكل تتالٍ. التتالٍ الأقرب إلى الكاميرا يغطي مساحة صغيرة من مساحة العالم وبالتالي لديه دقة فعالة عالية جدًا. تغطي التتاليات الأبعد مساحات أكبر تدريجيًا بنفس حجم النسيج، وهو أمر مقبول لأن هذه التفاصيل أقل وضوحًا للاعب.
التنفيذ: هذا أكثر تعقيدًا بشكل ملحوظ.
- في وحدة المعالجة المركزية، قسم مخروط الكاميرا إلى 2-4 تتاليات.
- لكل تتالٍ، قم بحساب مصفوفة إسقاط متعامدة ضيقة جدًا للضوء والتي تحيط بشكل مثالي بهذا القسم من المخروط.
- في حلقة العرض، قم بتنفيذ تمريرة العمق عدة مرات - مرة لكل تتالٍ، مع العرض إلى خريطة ظل مختلفة (أو منطقة من أطلس النسيج).
- في shader الأجزاء الخاص بتمريرة المشهد النهائية، حدد التتالٍ الذي ينتمي إليه الجزء الحالي بناءً على مسافته عن الكاميرا.
- خذ عينات من خريطة ظل التتالٍ المناسب لحساب الظل.
الفائدة: يوفر ظلالًا عالية الدقة باستمرار عبر مسافات واسعة، مما يجعله مثاليًا للبيئات الخارجية.
خرائط الظل المتباينة (VSM)
VSM هي تقنية أخرى لإنشاء ظلال ناعمة، لكنها تأخذ نهجًا مختلفًا عن PCF.
المفهوم: بدلاً من تخزين العمق فقط في خريطة الظل، يخزن VSM قيمتين: العمق (اللحظة الأولى) ومربع العمق (اللحظة الثانية). تسمح لنا هاتان القيمتان بحساب تباين توزيع العمق. باستخدام أداة رياضية تسمى متباينة تشيبيشيف، يمكننا تقدير احتمالية أن يكون الجزء في الظل. الميزة الرئيسية هي أن نسيج VSM يمكن تمويهه باستخدام الترشيح الخطي والتخريط ذي المستوى Mip المسرّع بالأجهزة القياسي، وهو أمر غير صالح رياضيًا لخريطة عمق قياسية. هذا يسمح بمناطق شبحية كبيرة جدًا وناعمة وسلسة بتكلفة أداء ثابتة.
العيب: يكمن العيب الرئيسي لـ VSM في "تسرب الضوء"، حيث يمكن أن يبدو أن الضوء يتسرب عبر الكائنات في المواقف التي تتداخل فيها الكائنات المحجوبة، حيث يمكن أن ينهار التقريب الإحصائي.
الفصل الخامس: نصائح عملية للتنفيذ والأداء
اختيار دقة خريطة الظل الخاصة بك
تعد دقة خريطة الظل الخاصة بك مفاضلة مباشرة بين الجودة والأداء. يوفر نسيج أكبر ظلالًا أكثر حدة ولكنه يستهلك المزيد من ذاكرة الفيديو ويستغرق وقتًا أطول للعرض وأخذ العينات. تشمل الأحجام الشائعة:
- 1024x1024: خط أساس جيد للعديد من التطبيقات.
- 2048x2048: يوفر تحسنًا ملحوظًا في الجودة للتطبيقات المكتبية.
- 4096x4096: جودة عالية، تستخدم غالبًا للأصول البطولية أو في المحركات ذات التجريد القوي.
تحسين مخروط الضوء
لتحقيق أقصى استفادة من كل بكسل في خريطة الظل الخاصة بك، من الضروري أن يكون حجم الإسقاط الخاص بالضوء (صندوقه المتعامد أو مخروط منظوره) مناسبًا تمامًا لعناصر المشهد التي تحتاج إلى ظلال. بالنسبة للضوء الاتجاهي، هذا يعني ملاءمة إسقاطه المتعامد لاحتواء الجزء المرئي فقط من مخروط الكاميرا. أي مساحة ضائعة في خريطة الظل هي دقة ضائعة.
ملحقات وإصدارات WebGL
WebGL 1 مقابل WebGL 2: بينما يكون تظليل الخرائط ممكنًا في WebGL 1، إلا أنه أسهل بكثير وأكثر كفاءة في WebGL 2. يتطلب WebGL 1 ملحق `WEBGL_depth_texture` لإنشاء نسيج عمق. يحتوي WebGL 2 على هذه الوظيفة مدمجة. علاوة على ذلك، يوفر WebGL 2 الوصول إلى عينات الظل (`sampler2DShadow`)، والتي يمكنها إجراء PCF مسرّع بالأجهزة، مما يوفر دفعة كبيرة في الأداء مقارنة بحلقات PCF اليدوية في الظل.
تصحيح أخطاء الظلال
يمكن أن تكون الظلال صعبة بشكل مخيف لتصحيح أخطائها. أهم تقنية مفيدة هي تصور خريطة الظل. قم بتعديل تطبيقك مؤقتًا لعرض نسيج العمق من مصدر ضوء معين مباشرة على رباعي على الشاشة. هذا يسمح لك برؤية ما "يراه" الضوء بالضبط. يمكن لهذا الكشف على الفور عن مشاكل في مصفوفات الضوء الخاصة بك، أو تجريد المخروط، أو عرض الكائن أثناء تمريرة العمق.
الخاتمة
يعد تظليل الخرائط في الوقت الفعلي حجر الزاوية في رسومات ثلاثية الأبعاد حديثة، حيث يحول المشاهد المسطحة الخالية من الحياة إلى عوالم واقعية وديناميكية. في حين أن مفهوم العرض من منظور الضوء بسيط، فإن تحقيق نتائج عالية الجودة وخالية من العيوب يتطلب فهمًا عميقًا للآليات الأساسية، من خط أنابيب المرور المزدوج إلى فروق التحيز العمق والتشويش.
من خلال البدء بتنفيذ أساسي، يمكنك معالجة العيوب الشائعة مثل حب الشباب الظل والحواف المتعرجة بشكل تدريجي. من هناك، يمكنك الارتقاء بمرئياتك باستخدام تقنيات متقدمة مثل PCF للظلال الناعمة أو خرائط الظل المتتالية للبيئات واسعة النطاق. تعد رحلة عرض الظل مثالًا مثاليًا لمزيج الفن والعلم الذي يجعل رسومات الكمبيوتر مقنعة للغاية. نشجعك على تجربة هذه التقنيات، ودفع حدودها، وجلب مستوى جديد من الواقعية إلى مشاريع WebGL الخاصة بك.